package kmeans;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.JFrame;


class Cluster {
    List<Point> points = new ArrayList<>();
    Point centroid;

    Cluster(Point initialCentroid) {
        this.centroid = initialCentroid;
    }

    void addPoint(Point point) {
        points.add(point);
        point.set_Cluster(this);
    }

    void recomputeCentroid() {
        double sumX = 0, sumY = 0;
        for (Point point : points) {
            sumX += point.x;
            sumY += point.y;
        }
        centroid = new Point(sumX / points.size(), sumY / points.size());
    }
}

public class KMeans implements Callable<List<Cluster>>{
    private static int K = 3; // 假设我们要创建3个簇
    private static final int MAX_ITERATIONS = 1000; // 最大迭代次数
    private List<Point> points= new ArrayList<Point>();

    KMeans(int K) {
        KMeans.K = K;
    }

    public void setPoints(List<Point> points) {
        for (Point p : points){
            this.points.add(p.clone());
        }
    }

    private List<Cluster> generate_seeds(int K) {
        Random random = new Random();
        // 随机选择K个初始簇中心
        List<Cluster> clusters = new ArrayList<>();
        do {
            // 随机选择一个点作为初始簇中心
            Point initialCentroid = new Point(random.nextDouble() * 10, random.nextDouble() * 10);
            double minDistance = Double.MAX_VALUE;
            // 判断簇中心之间的距离达到一定距离
            for (Cluster cluster : clusters) {
                double distance = initialCentroid.distanceTo(cluster.centroid);
                if (distance < minDistance) {
                    minDistance = distance;
                }
            }
            if (minDistance > 5) {
                clusters.add(new Cluster(initialCentroid));
            }
        } while (clusters.size() < K);
        return clusters;
    }

    @Override
    public List<Cluster> call(){
        List<Point> points = this.points;
        // 随机选择K个初始簇中心
        List<Cluster> clusters = this.generate_seeds(K);

        // 执行K-Means算法
        for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
            boolean centroidsMoved = false;
            for (Point point : points) {
                Cluster closestCluster = null;
                double minDistance = Double.MAX_VALUE;
                for (Cluster cluster : clusters) {
                    double distance = point.distanceTo(cluster.centroid);
                    if (distance < minDistance) {
                        minDistance = distance;
                        closestCluster = cluster;
                    }
                }
                if (!closestCluster.points.contains(point)) {
                    if (point.get_Cluster() != null) {
                        // 如果点已经属于其他簇，则将其从原簇中移除
                        point.get_Cluster().points.remove(point);
                    }
                    closestCluster.addPoint(point);
                    centroidsMoved = true;
                }
            }

            for (Cluster cluster : clusters) {
                cluster.recomputeCentroid();
            }

            if (!centroidsMoved) {
                // 如果簇中心没有移动，则算法已经收敛
                break;
            }
        }
        System.out.printf("K-Means算法执行完毕! err:%f \n", KMeans.calculate_error(clusters));

        return clusters;
    }

    public static double calculate_error(List<Cluster>clusters){
        double error = 0;

        for (Cluster cluster : clusters) {
            for (Point point : cluster.points) {
                error += point.distanceTo(cluster.centroid);
            }
        }
        return error;
    }

    public static void main(String[] args) {
        Dataset dataset = new Dataset(K);
        // 创建一些随机点作为数据集
        List<Point> points = dataset.load_data("sample_data.csv");

        int numberOfThreads = 5;
        //多线程操作
        ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
        List<Future<List<Cluster>>> futures = new ArrayList<>();

        
        for (int i = 0; i < 10; i++) {
            KMeans kmeans = new KMeans(K);
            kmeans.setPoints(points);
            futures.add(executor.submit(kmeans));
        }

        executor.shutdown(); // 关闭ExecutorService

        while (!executor.isTerminated()) {
            try {
                System.out.println("任务正在执行中");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } 
        }
        System.out.println("所有任务已完成");

        // 寻找并写入最佳结果
        double best_error = Double.MAX_VALUE;
        List<Cluster> best_clusters = null;
        try {
            for (Future<List<Cluster>> future : futures) {
                List<Cluster> clusters = future.get();
                double error = KMeans.calculate_error(clusters);
                if (error < best_error) {
                    best_error = error;
                    best_clusters = clusters;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 输出结果
        for (int i = 0; i < best_clusters.size(); i++) {
            System.out.println("Cluster " + i + ":");
            for (Point point : best_clusters.get(i).points) {
                System.out.println("(" + point.x + ", " + point.y + ")");
            }
        }
        System.out.println("Best Error: " + best_error);

        KMeans.save_png(best_clusters);
    }

    private static void save_png(List<Cluster> clusters) {
        ScatterPlot example = new ScatterPlot();
        example.draw(clusters);
        example.pack();
        example.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        example.setVisible(true);
    }
}